在寫網頁的時候,我們常常會需要監聽使用者的操作來觸發一些行為,而使用者的某個操作(如點擊按鈕、按下鍵盤…)就叫做事件(Event)
最常見的例子就是按下一個按鈕的點擊事件
一開始學的時候,都會直接寫在HTML裡,這樣很不好維護
<button onclick="alert('Hello!')">Click me</button>
但這樣很不好維護,所以當我們學了JS,就把它寫在JS
<button id="btn">Click me</button>
const btn = document.getElementById("btn");
btn.onclick = function () {
alert("Hello!");
};
但這種方式有個問題:它只能指定一個事件處理函式,後面會蓋掉前面,像下面這樣
btn.onclick = function () {
alert("First");
};
btn.onclick = function () {
alert("Second"); // 第一個被蓋掉了
};
所以我們就學了addEventListener
btn.addEventListener("click", () => {
alert("First");
});
btn.addEventListener("click", () => {
alert("Second");
});
這兩個都會執行!
這是因為他綁定的方式支援:
當你點擊一個 HTML 元素時,事件不會只發生在那個元素身上,它會從你點的元素往上傳遞(Bubble),也可以設定成從最外層往內傳遞(Capture)
這就叫做事件傳遞機制,它包含三個階段:
看一個例子:
<div id="outer">
<button id="inner">Click me</button>
</div>
document.getElementById("outer").addEventListener(
"click",
() => {
console.log("Outer clicked");
},
false // false 表示在冒泡階段處理
);
document.getElementById("inner").addEventListener(
"click",
() => {
console.log("Inner clicked");
},
false
);
點下 button,會看到:
Inner clicked
Outer clicked
這表示事件先從 inner
開始觸發,然後再冒泡到 outer
那如果改成true
,就會變成Capture
document.getElementById("outer").addEventListener(
"click",
() => {
console.log("Outer clicked");
},
true // true 表示在捕獲階段處理
);
document.getElementById("inner").addEventListener(
"click",
() => {
console.log("Inner clicked");
},
true
);
console就會長這樣,先Outer再Inner
Outer clicked
Inner clicked
比較一下
傳遞階段 | 執行順序 | 參數 |
---|---|---|
捕獲 | 外 → 內 | 第三個參數設 true |
冒泡 | 內 → 外 | 第三個參數設 false (預設) |
這種事件傳遞機制,讓我們可以做事件代理(Event Delegation),透過把事件只綁在外層,來管理內部多個元素的事件
例如:
<ul id="list">
<li class="item">1</li>
<li class="item">2</li>
</ul>
document.getElementById("list").addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
alert(`Clicked ${e.target.textContent}`);
}
});
// 注意:
// 這樣寫是錯的,因為click只會在點擊的時候執行,但是迴圈會先跑完
// const items = document.querySelectorAll('.item')
// for (i = 0; i < btns.length; i += 1) {
// items[i].addEventListener('click', () => {
// alert(i)
// })
// }
這樣新增很多 <li>
,都不用一個個綁定!
Note:
e
是事件物件(event object),是瀏覽器在事件發生時自動傳進來的參數,裡面很多關於這次事件的細節,像是這次的事件類型(type:"click"
)、觸發事件的元素(target:li
)、滑鼠相對於視窗的座標(clientX: 51
、clientY: 82
)…
Note:阻止冒泡或捕獲
e.stopPropagation();
這個方法可以停止事件繼續冒泡或捕獲,就是不要再讓事件傳下去了
https://ithelp.ithome.com.tw/articles/10191970
https://hackmd.io/@Heidi-Liu/note-fe201-dom
https://www.explainthis.io/zh-hant/swe/fe-event-delegation-capture-bubble